﻿using System;
using System.Linq.Expressions;

namespace MathService.MathParser.ExpressionTrees.Nodes
{
    /// <summary>Узел дерева мат.выражения, реазлиующий оператор отрицания</summary>
    public class NotOperatorNode : LogicOperatorNode
    {
        /// <summary>Инициализация нового узла оператора меньше</summary>
        public NotOperatorNode() : base("!", -20) { }

        /// <summary>Инициализация нового узла оператора меньше</summary>
        /// <param name="Left">Левое поддерево выражения</param>
        /// <param name="Right">Правое поддерево выражения</param>
        public NotOperatorNode(ExpressionTreeNode Left, ExpressionTreeNode Right)
            : this()
        {
            this.Left = Left;
            this.Right = Right;
        }

        /// <summary>Метод отрицания</summary>
        /// <param name="x">Первое значение</param>
        /// <param name="y">Второе значение</param>
        /// <returns>1 - если x меньше y и 0 - в противном случае</returns>
        private static double Comparer(double x, double y) { return Math.Abs(x - y) >= EqualityOperatorNode.sf_Epsilon ? 1 : 0; }

        /// <summary>Метод отрицания</summary>
        /// <param name="x">Значение</param>
        /// <returns>1 - если x меньше y и 0 - в противном случае</returns>
        private static double ComparerSingle(double x) { return Math.Abs(x) < EqualityOperatorNode.sf_Epsilon ? 1 : 0; }

        /// <summary>Вычисление значения узла</summary>
        /// <returns>0 - если разность между x и y по модулю меньше Epsilon и 1 во всех остальных случаях</returns>
        public override double Compute()
        {
            return Left == null
                        ? ComparerSingle(((ComputedNode)Right).Compute())
                        : Comparer(((ComputedNode)Left).Compute(), ((ComputedNode)Right).Compute());
        }

        /// <summary>Компиляция логики узла</summary>
        /// <returns>Скомпилированное логическое выражение, реализующее операцию отрицания НЕ</returns>
        public override Expression LogicCompile()
        {
            if(Left == null)
                return Right is LogicOperatorNode
                    ? (Expression)Expression.Not(((LogicOperatorNode)Right).LogicCompile())
                    : Expression.LessThan
                        (
                            EqualityOperatorNode.GetAbsMethodCall(((ComputedNode)Right).Compile()),
                            Expression.Constant(EqualityOperatorNode.sf_Epsilon)
                        );

            return Left is LogicOperatorNode && Right is LogicOperatorNode
                ? Expression.NotEqual(((LogicOperatorNode)Left).LogicCompile(), ((LogicOperatorNode)Right).LogicCompile())
                : Expression.GreaterThanOrEqual
                    (
                        EqualityOperatorNode.GetAbsMethodCall(Expression.Subtract
                        (
                            ((ComputedNode)Left).Compile(),
                            ((ComputedNode)Right).Compile())
                        ),
                        Expression.Constant(EqualityOperatorNode.sf_Epsilon)
                    );
        }

        /// <summary>Компиляция логики узла</summary>
        /// <param name="Parameters">Параметры компиляции</param>
        /// <returns>Скомпилированное логическое выражение, реализующее операцию отрицания НЕ</returns>
        public override Expression LogicCompile(ParameterExpression[] Parameters)
        {
            if(Left == null)
                return Right is LogicOperatorNode
                    ? (Expression)Expression.Not(((LogicOperatorNode)Right).LogicCompile(Parameters))
                    : Expression.LessThan
                        (
                            EqualityOperatorNode.GetAbsMethodCall(((ComputedNode)Right).Compile(Parameters)),
                            Expression.Constant(EqualityOperatorNode.sf_Epsilon)
                        );

            return Left is LogicOperatorNode && Right is LogicOperatorNode
                ? Expression.NotEqual(((LogicOperatorNode)Left).LogicCompile(Parameters), ((LogicOperatorNode)Right).LogicCompile(Parameters))
                : Expression.GreaterThanOrEqual
                    (
                        EqualityOperatorNode.GetAbsMethodCall(Expression.Subtract
                        (
                            ((ComputedNode)Left).Compile(Parameters),
                            ((ComputedNode)Right).Compile(Parameters))
                        ),
                        Expression.Constant(EqualityOperatorNode.sf_Epsilon)
                    );
        }

        /// <summary>Строковое представление узла</summary>
        /// <returns>Строковое представление узла</returns>
        public override string ToString()
        {
            return Right == null ? base.ToString() : string.Format("{0}≠{1}", Left, Right);
        }

        /// <summary>Клонирование узла</summary>
        /// <returns>Клон узла</returns>
        public override ExpressionTreeNode Clone() { return CloneOperatorNode<NotOperatorNode>(); }
    }
}